package shaw.wifi.nohttp;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import shaw.wifi.Constants;
import shaw.wifi.NetworkSettingsTaskManager;
import shaw.wifi.network.ConnectionInformation;
import android.util.Log;

public class NetworkSettingsAuthTask extends NetworkSettingsNoHttpTask {
	@Override
	protected Integer doInBackground(ConnectionInformation... params) {
		if(!setupSocketConnection()) {
			NetworkSettingsTaskManager.get().clearTasks();
			return null;
		}
		
		boolean result = authenticateServer();
		if(result == false) {
			NetworkSettingsTaskManager.get().clearTasks();
		}
		return null;
	}

	private String challengeString;
	private byte[] cryptBytes;
	
	private boolean authenticateServer() {
		if(setupChallenge() == false) {
			return false;
		}
		
		ShawWifiMessage msg = new ShawWifiMessage(2);
		msg.type = ShawWifiMessage.TYPE_AUTH;
		msg.header[0] = "action";
		msg.data[0] = "authenticate".getBytes();
		msg.header[1] = "challenge";
		msg.data[1] = cryptBytes;
		
		try {
			msg.sendMessage(oos);
		} catch (IOException e) {
			publishProgress("Cannot send challenge to server");
			e.printStackTrace();
			return false;
		}
		
		ShawWifiMessage resp = this.waitForMessage();
		if(resp == null) {
			publishProgress("Did not recieve challenge result from server");
			return false;
		}
		
		try {
			if(resp.length != 1) throw new UnexpectedMessageException("expected 1 header");
			if(!resp.header[0].equals("RESULT")) throw new UnexpectedMessageException("expected header RESULT" + resp.header[0]);
			
			String result = new String(resp.data[0]);
			Log.e("AUTH", "Response recieved: " + result + " : " + challengeString);
			if(result.equals(this.challengeString)) {
				publishProgress("Server authenticated");
				return true;
			} else {
				publishProgress("Server authentication failed");
				return false;
			}
		} catch(UnexpectedMessageException e) {
			e.printStackTrace();
			publishProgress("Received unexpected message from server, please make sure you have the most recent version of the application");
			return false;
		}
	}

	private boolean setupChallenge() {
		PublicKey publicKey;
		RSAPublicKeySpec spec = new RSAPublicKeySpec(Constants._RSAMOD, Constants._RSAEXP);
		try {
			KeyFactory keyFact = KeyFactory.getInstance("RSA", "BC");
			publicKey = keyFact.generatePublic(spec);
			publishProgress("Got RSA Key...");
		} catch (NoSuchAlgorithmException Exc) {
			publishProgress("Cannot get RSA key (NSA)...");
			Exc.printStackTrace();
			return false;
		} catch (InvalidKeySpecException Exc) {
			publishProgress("Cannot generate RSA key (Invalid)...");
			Exc.printStackTrace();
			return false;
		} catch (NoSuchProviderException Exc) {
			publishProgress("No support for BC...");
			Exc.printStackTrace();
			return false;
		}
		String randomString = String.valueOf((long) (Math.random() * (double)Long.MAX_VALUE)) + String.valueOf((long) (Math.random() * (double)Long.MAX_VALUE));
		Log.e("AUTH", "issued challenge string " + randomString);
		
		try {
			Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
			c.init(Cipher.ENCRYPT_MODE, publicKey);
			Log.e("ENCR", c.getBlockSize() + "");
			challengeString = randomString;
			cryptBytes = c.doFinal(randomString.getBytes());
		
			return true;
		} catch (InvalidKeyException e) {
			publishProgress("Cannot encrypt message (invalid key)...");
			e.printStackTrace();
			return false;
		} catch (NoSuchAlgorithmException e) {
			publishProgress("Cannot encrypt message (NSA)...");
			e.printStackTrace();
			return false;
		} catch (NoSuchPaddingException e) {
			publishProgress("Cannot encrypt message (NSP)...");
			e.printStackTrace();
			return false;
		} catch (IllegalBlockSizeException e) {
			publishProgress("Message too large for key...");
			e.printStackTrace();
			return false;
		} catch (BadPaddingException e) {
			publishProgress("Bad message padding...");
			e.printStackTrace();
			return false;
		} catch (NoSuchProviderException e) {
			e.printStackTrace();
			return false;
		}	
	}
}
